/**
 * 2015，thor.com，all rights reserved。
 * 
 * Project：		total-core
 * Filename：	MySQLSequenceGenerator.java
 * Created：		2015年12月16日
 * Creator：		thor
 */
package com.thor.palmmall.util.billnumber;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.hibernate.ejb.HibernateEntityManager;
import org.slf4j.LoggerFactory;

import com.thor.commons.util.Assert;
import com.thor.dao.BasicDao;

/**
 * @author thor
 *
 */
public class MySQLSequenceGenerator extends BasicDao implements ISequenceGenerator {
	/** 序列的前缀 */
	public static final String PREFIX_SEQ = "SPSeq";
	/** 序列名需要满足的规则 */
	private static final String PATTERN_DBID = "^[\\w]+$";
	/** 序列名的最大长度 */
	private static final int MAX_DBID_LENGTH = 24;

	public long nextValue(SequenceSpec seq) throws Exception {
		Assert.assertArgumentNotNull(seq, "seq");
		Assert.assertArgumentNotNull(seq.getDbId(), "seq.dbid");

		if (!java.util.regex.Pattern.matches(PATTERN_DBID, seq.getDbId()))
			throw new IllegalArgumentException("数据库序列标识只能由数字或字母组成。");
		if (seq.getDbId().length() > MAX_DBID_LENGTH)
			throw new IllegalArgumentException("数据库序列标识不能超过 " + MAX_DBID_LENGTH + " 位。");
		if (seq.getStartValue() <= 0)
			throw new IllegalArgumentException("数据库序列的起始值必须大于0。");

		try {
			return fetchNext(seq);
		} catch (Exception e) {
			try {
				tryCreateSeq(seq);
			} catch (SQLException ex) {
				throw new Exception(ex);
			}
			return fetchNext(seq);
		}
	}

	private long fetchNext(SequenceSpec seq) throws Exception {
		List<Integer> list = em.createNativeQuery("select id  from " + getSeqName(seq) + " order by id desc limit 1").getResultList();
		Connection dbConn = ((HibernateEntityManager) em).getSession().connection();
		Statement st = null;
		try {
			dbConn.setAutoCommit(false);
			st = dbConn.createStatement();
			st.executeUpdate("insert into " + getSeqName(seq) + " select NULL");
		} catch (SQLException e) {
			LoggerFactory.getLogger(this.getClass()).error("取下一流水号失败。", e);
			dbConn.rollback();
			throw e;
		} finally {
			if (st != null)
				st.close();
			dbConn.setAutoCommit(true);
			((HibernateEntityManager) em).getSession().disconnect();
		}
		if (list.isEmpty() || list.get(0) > seq.getMaxValue())
			throw new Exception("取下一流水号失败。");
		return ((Number) list.get(0)).longValue();
	}

	@SuppressWarnings("deprecation")
	private void tryCreateSeq(SequenceSpec seq) throws SQLException {
		Connection dbConn = ((HibernateEntityManager) em).getSession().connection();
		Statement st = null;
		try {
			dbConn.setAutoCommit(false);
			st = dbConn.createStatement();
			st.executeUpdate("create table if not exists " + getSeqName(seq) + "(id int not null auto_increment primary key)");
			st.executeUpdate("alter table " + getSeqName(seq) + " auto_increment=" + seq.getStartValue());
			st.executeUpdate("insert into " + getSeqName(seq) + " select NULL");
		} catch (SQLException e) {
			LoggerFactory.getLogger(this.getClass()).error("创建序列失败。", e);
			dbConn.rollback();
			throw e;
		} finally {
			if (st != null)
				st.close();
			dbConn.setAutoCommit(true);
			((HibernateEntityManager) em).getSession().disconnect();
		}
	}

	private String getSeqName(SequenceSpec seq) {
		return PREFIX_SEQ + seq.getDbId();
	}

}
